<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <title>Android应用程序线程消息循环模型分析 - 老罗的Android之旅
        - 博客频道 - CSDN.NET</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="description" content="我们知道，Android应用程序是通过消息来驱动的，即在应用程序的主线程（UI线程）中有一个消息循环，负责处理消息队列中的消息。我们也知道，Android应用程序是支持多线程的，即可以创建子线程来执行一些计算型的任务，那么，这些子线程能不能像应用程序的主线程一样具有消息循环呢？这些子线程又能不能往应用程序的主线程中发送消息呢？本文将分析Android应用程序线程消息处理模型，为读者解答这两个问题" />
    <script src="http://static.blog.csdn.net/scripts/jquery.js" type="text/javascript"></script>
    <script type="text/javascript" src="http://static.blog.csdn.net/scripts/ad.js?v=1.1"></script>
        <!--new top-->
               <link rel="stylesheet" href="http://static.csdn.net/public/common/toolbar/css/index.css">        <!--new top-->

    <link rel="Stylesheet" type="text/css" href="http://static.blog.csdn.net/skin/default/css/style.css?v=1.1" />
    <link id="RSSLink" title="RSS" type="application/rss+xml" rel="alternate" href="/Luoshengyang/rss/list" />
    <link rel="shortcut icon" href="/favicon.ico" />
    <link type="text/css" rel="stylesheet" href="http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/default.css" />
 

</head>
<body>
    
   
      <!--new top-->
    <script id="toolbar-tpl-scriptId" fixed="true" prod="blog" skin="black" src="http://static.csdn.net/public/common/toolbar/js/html.js" type="text/javascript"></script>
     <!--new top-->
    <div id="container">
        <div id="header">
    <div class="header">
        <div id="blog_title">
            <h2>
                <a href="http://blog.csdn.net/luoshengyang">老罗的Android之旅</a></h2>
            <h3>爱生活，爱Android</h3>
            <div class="clear">
            </div>
        </div>
        <div class="clear">
        </div>
    </div>
</div>
<div id="navigator">
    <div class="navigator_bg">
    </div>
    <div class="navigator">
        <ul>
                <li id="btnContents"><a href="http://blog.csdn.net/luoshengyang?viewmode=contents"><span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_mulu'])">
                    <img src="http://static.blog.csdn.net/images/ico_list.gif">目录视图</span></a></li>
                <li id="btnView"><a href="http://blog.csdn.net/luoshengyang?viewmode=list"><span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_zhaiyao'])">
                    <img src="http://static.blog.csdn.net/images/ico_summary.gif">摘要视图</span></a></li>
                <li id="btnRss"><a href="http://blog.csdn.net/luoshengyang/rss/list"><span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_RSS'])">
                    <img src="http://static.blog.csdn.net/images/ico_rss.gif">订阅</span></a></li>
</ul>
    </div>
</div>
<script type="text/javascript">
    var username = "Luoshengyang";
    var _blogger = username;
    var blog_address = "http://blog.csdn.net/luoshengyang";
    var static_host = "http://static.blog.csdn.net";
    var currentUserName = "";
</script>

        <div id="body">
            <div id="main">
                <div class="main">
<div class="notice tracking-ad" data-mod='popu_3' > 


<a href="http://bss.csdn.net/cview/reg/?project_id=804&identy_id=839" target="_blank"><font color=red>“程序人生”中国软件开发者职业生涯调查
</font></a>

&nbsp;&nbsp;&nbsp;&nbsp;


<a href="http://bbs.csdn.net/topics/390724670"target="_blank">
<font color=red>CSDN社区“三八节”特别活动</font></a>
&nbsp;&nbsp;&nbsp;&nbsp;


<a href="http://www.csdn.net/article/2014-03-06/2818653"target="_blank">
<font color=blue>开发者职业生涯调查之未来</font></a>




</div>                    <link href="http://static.blog.csdn.net/css/comment1.css" type="text/css" rel="stylesheet" />
<link href="http://static.blog.csdn.net/css/style1.css" type="text/css" rel="stylesheet" />
<div id="article_details" class="details">
    <div class="article_title">
    <span class="ico ico_type_Original"></span>
    <h1>
        <span class="link_title"><a href="/luoshengyang/article/details/6905587">
        Android应用程序线程消息循环模型分析
        </a></span>
    </h1>
</div>

    <div class="article_manage">
        <span class="link_categories">
        分类：
            <a href="/Luoshengyang/article/category/838604">Android</a> 
        </span>
    <span class="link_postdate">2011-10-31 01:00</span>
    <span class="link_view" title="阅读次数">21957人阅读</span>
    <span class="link_comments" title="评论次数"><a href="#comments" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_pinglun'])">评论</a>(36)</span>
    <span class="link_collect"><a href="javascript:void(0);" onclick="javascript:_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_shoucang']);collectArticle('Android应用程序线程消息循环模型分析','6905587');return false;" title="收藏">收藏</a></span>
    <span class="link_report"><a href="#report"  onclick="javascript:_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_jubao']);report(6905587,2);return false;" title="举报">举报</a></span>
    
</div>
<div class="tag2box"><a href='http://www.csdn.net/tag/android' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">android</a><a href='http://www.csdn.net/tag/%e4%bb%bb%e5%8a%a1' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">任务</a><a href='http://www.csdn.net/tag/layout' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">layout</a><a href='http://www.csdn.net/tag/thread' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">thread</a><a href='http://www.csdn.net/tag/integer' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">integer</a></div>

    
<div id="article_content" class="article_content">
<p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;我们知道，Android应用程序是通过消息来驱动的，即在应用程序的主线程（UI线程）中有一个消息循环，负责处理消息队列中的消息。我们也知道，Android应用程序是支持多线程的，即可以创建子线程来执行一些计算型的任务，那么，这些子线程能不能像应用程序的主线程一样具有消息循环呢？这些子线程又能不能往应用程序的主线程中发送消息呢？本文将分析Android应用程序线程消息处理模型，为读者解答这两个问题</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在开发Android应用程序中，有时候我们需要在应用程序中创建一些常驻的子线程来不定期地执行一些不需要与应用程序界面交互的计算型的任务。如果这些子线程具有消息循环，那么它们就能够常驻在应用程序中不定期的执行一些计算型任务了：当我们需要用这些子线程来执行任务时，就往这个子线程的消息队列中发送一个消息，然后就可以在子线程的消息循环中执行我们的计算型任务了。我们在前面一篇文章<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6767736">Android系统默认Home应用程序（Launcher）的启动过程源代码分析</a>中，介绍Launcher的启动过程时，在Step 15（LauncherModel.startLoader）中，Launcher就是通过往一个子线程的消息队列中发送一个消息（sWorker.post(mLoaderTask)），然后子线程就会在它的消息循环中处理这个消息的时候执行从PackageManagerService中获取系统中已安装应用程序的信息列表的任务，即调用Step 16中的LoaderTask.run函数。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在开发Android应用程序中，有时候我们又需要在应用程序中创建一些子线程来执行一些需要与应用程序界面进交互的计算型任务。典型的应用场景是当我们要从网上下载文件时，为了不使主线程被阻塞，我们通常创建一个子线程来负责下载任务，同时，在下载的过程，将下载进度以百分比的形式在应用程序的界面上显示出来，这样就既不会阻塞主线程的运行，又能获得良好的用户体验。但是，我们知道，Android应用程序的子线程是不可以操作主线程的UI的，那么，这个负责下载任务的子线程应该如何在应用程序界面上显示下载的进度呢？如果我们能够在子线程中往主线程的消息队列中发送消息，那么问题就迎刃而解了，因为发往主线程消息队列的消息最终是由主线程来处理的，在处理这个消息的时候，我们就可以在应用程序界面上显示下载进度了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;上面提到的这两种情况，Android系统都为我们提供了完善的解决方案，前者可以通过使用HandlerThread类来实现，而后者可以使用AsyncTask类来实现，本文就详细这两个类是如何实现的。不过，为了更好地理解HandlerThread类和AsyncTask类的实现，我们先来看看应用程序的主线程的消息循环模型是如何实现的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;1. 应用程序主线程消息循环模型</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在前面一篇文章<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6747696">Android应用程序进程启动过程的源代码分析</a>一文中，我们已经分析应用程序进程（主线程）的启动过程了，这里主要是针对它的消息循环模型作一个总结。当运行在Android应用程序框架层中的ActivityManagerService决定要为当前启动的应用程序创建一个主线程的时候，它会在ActivityManagerService中的startProcessLocked成员函数调用Process类的静态成员函数start为当前应用程序创建一个主线程：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_1_2985558"  code_snippet_id="130106" snippet_file_name="blog_20131227_1_2985558" name="code" class="java">public final class ActivityManagerService extends ActivityManagerNative    
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {    
    
    ......    
    
    private final void startProcessLocked(ProcessRecord app,    
                String hostingType, String hostingNameStr) {    
    
        ......    
    
        try {    
            int uid = app.info.uid;    
            int[] gids = null;    
            try {    
                gids = mContext.getPackageManager().getPackageGids(    
                    app.info.packageName);    
            } catch (PackageManager.NameNotFoundException e) {    
                ......    
            }    
                
            ......    
    
            int debugFlags = 0;    
                
            ......    
                
            int pid = Process.start(&quot;android.app.ActivityThread&quot;,    
                mSimpleProcessManagement ? app.processName : null, uid, uid,    
                gids, debugFlags, null);    
                
            ......    
    
        } catch (RuntimeException e) {    
                
            ......    
    
        }    
    }    
    
    ......    
    
}    </pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里我们主要关注Process.start函数的第一个参数“android.app.ActivityThread”，它表示要在当前新建的线程中加载android.app.ActivityThread类，并且调用这个类的静态成员函数main作为应用程序的入口点。ActivityThread类定义在frameworks/base/core/java/android/app/ActivityThread.java文件中：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_2_2222190"  code_snippet_id="130106" snippet_file_name="blog_20131227_2_2222190" name="code" class="java">public final class ActivityThread {  
    ......  
  
    public static final void main(String[] args) {  
        ......
  
        Looper.prepareMainLooper();  
         
        ......  
  
        ActivityThread thread = new ActivityThread();  
        thread.attach(false);  
  
        ...... 
        Looper.loop();  
  
        ...... 
  
        thread.detach();  
        ......  
    }  
  
    ......  
}  </pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在这个main函数里面，除了创建一个ActivityThread实例外，就是在进行消息循环了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在进行消息循环之前，首先会通过Looper类的静态成员函数prepareMainLooper为当前线程准备一个消息循环对象。Looper类定义在frameworks/base/core/java/android/os/Looper.java文件中：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_3_7588146"  code_snippet_id="130106" snippet_file_name="blog_20131227_3_7588146" name="code" class="java">public class Looper {
	......

	// sThreadLocal.get() will return null unless you've called prepare().
	private static final ThreadLocal sThreadLocal = new ThreadLocal();

	......

	private static Looper mMainLooper = null;

	......

	public static final void prepare() {
		if (sThreadLocal.get() != null) {
			throw new RuntimeException(&quot;Only one Looper may be created per thread&quot;);
		}
		sThreadLocal.set(new Looper());
	}

	......

	public static final void prepareMainLooper() {
		prepare();
		setMainLooper(myLooper());
		......
	}

	private synchronized static void setMainLooper(Looper looper) {
		mMainLooper = looper;
	}

	public synchronized static final Looper getMainLooper() {
		return mMainLooper;
	}

	......

	public static final Looper myLooper() {
		return (Looper)sThreadLocal.get();
	}

	......
}</pre><p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Looper类的静态成员函数prepareMainLooper是专门应用程序的主线程调用的，应用程序的其它子线程都不应该调用这个函数来在本线程中创建消息循环对象，而应该调用prepare函数来在本线程中创建消息循环对象，下一节我们介绍一个线程类HandlerThread 时将会看到。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;为什么要为应用程序的主线程专门准备一个创建消息循环对象的函数呢？这是为了让其它地方能够方便地通过Looper类的getMainLooper函数来获得应用程序主线程中的消息循环对象。获得应用程序主线程中的消息循环对象又有什么用呢？一般就是为了能够向应用程序主线程发送消息了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在prepareMainLooper函数中，首先会调用prepare函数在本线程中创建一个消息循环对象，然后将这个消息循环对象放在线程局部变量sThreadLocal中：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_4_5570260"  code_snippet_id="130106" snippet_file_name="blog_20131227_4_5570260" name="code" class="java">sThreadLocal.set(new Looper());</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;接着再将这个消息循环对象通过调用setMainLooper函数来保存在Looper类的静态成员变量mMainLooper中：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_5_5919707"  code_snippet_id="130106" snippet_file_name="blog_20131227_5_5919707" name="code" class="java">mMainLooper = looper;</pre>&nbsp;&nbsp; &nbsp; &nbsp; 这样，其它地方才可以调用getMainLooper函数来获得应用程序主线程中的消息循环对象。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 消息循环对象创建好之后，回到ActivityThread类的main函数中，接下来，就是要进入消息循环了：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_6_5538023"  code_snippet_id="130106" snippet_file_name="blog_20131227_6_5538023" name="code" class="java"> Looper.loop(); </pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Looper类具体是如何通过loop函数进入消息循环以及处理消息队列中的消息，可以参考前面一篇文章<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6817933">Android应用程序消息处理机制（Looper、Handler）分析</a>，这里就不再分析了，我们只要知道ActivityThread类中的main函数执行了这一步之后，就为应用程序的主线程准备好消息循环就可以了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;2. 应用程序子线程消息循环模型</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在Java框架中，如果我们想在当前应用程序中创建一个子线程，一般就是通过自己实现一个类，这个类继承于Thread类，然后重载Thread类的run函数，把我们想要在这个子线程执行的任务都放在这个run函数里面实现。最后实例这个自定义的类，并且调用它的start函数，这样一个子线程就创建好了，并且会调用这个自定义类的run函数。但是当这个run函数执行完成后，子线程也就结束了，它没有消息循环的概念。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;前面说过，有时候我们需要在应用程序中创建一些常驻的子线程来不定期地执行一些计算型任务，这时候就可以考虑使用Android系统提供的HandlerThread类了，它具有创建具有消息循环功能的子线程的作用。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;HandlerThread类实现在frameworks/base/core/java/android/os/HandlerThread.java文件中，这里我们通过使用情景来有重点的分析它的实现。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在前面一篇文章<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6767736">Android系统默认Home应用程序（Launcher）的启动过程源代码分析</a>中，我们分析了Launcher的启动过程，其中在Step 15（LauncherModel.startLoader）和Step 16（LoaderTask.run）中，Launcher会通过创建一个HandlerThread类来实现在一个子线程加载系统中已经安装的应用程序的任务：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_7_7108186"  code_snippet_id="130106" snippet_file_name="blog_20131227_7_7108186" name="code" class="java">public class LauncherModel extends BroadcastReceiver {
	......

	private LoaderTask mLoaderTask;

	private static final HandlerThread sWorkerThread = new HandlerThread(&quot;launcher-loader&quot;);
	static {
		sWorkerThread.start();
	}
	private static final Handler sWorker = new Handler(sWorkerThread.getLooper());

	......

	public void startLoader(Context context, boolean isLaunching) {  
		......  

		synchronized (mLock) {  
			......  

			// Don't bother to start the thread if we know it's not going to do anything  
			if (mCallbacks != null &amp;&amp; mCallbacks.get() != null) {  
				......

				mLoaderTask = new LoaderTask(context, isLaunching);  
				sWorker.post(mLoaderTask);  
			}  
		}  
	}  

	......

	private class LoaderTask implements Runnable {  
		......  

		public void run() {  
			......  

			keep_running: {  
				......  

				// second step  
				if (loadWorkspaceFirst) {  
					......  
					loadAndBindAllApps();  
				} else {  
					......  
				}  

				......  
			}  

			......  
		}  

		......  
	} 

	......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在这个LauncherModel类中，首先创建了一个HandlerThread对象：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_8_5505786"  code_snippet_id="130106" snippet_file_name="blog_20131227_8_5505786" name="code" class="java">private static final HandlerThread sWorkerThread = new HandlerThread(&quot;launcher-loader&quot;);</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;接着调用它的start成员函数来启动一个子线程：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_9_2615847"  code_snippet_id="130106" snippet_file_name="blog_20131227_9_2615847" name="code" class="java">static {
    sWorkerThread.start();
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;接着还通过这个HandlerThread对象的getLooper函数来获得这个子线程中的消息循环对象，并且使用这个消息循环创建对象来创建一个Handler：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_10_2234163"  code_snippet_id="130106" snippet_file_name="blog_20131227_10_2234163" name="code" class="java">private static final Handler sWorker = new Handler(sWorkerThread.getLooper());</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;有了这个Handler对象sWorker之后，我们就可以往这个子线程中发送消息，然后在处理这个消息的时候执行加载系统中已经安装的应用程序的任务了，在startLoader函数中：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_11_3804327"  code_snippet_id="130106" snippet_file_name="blog_20131227_11_3804327" name="code" class="java">mLoaderTask = new LoaderTask(context, isLaunching);  
sWorker.post(mLoaderTask);  </pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里的mLoaderTask是一个LoaderTask对象，它实现了Runnable接口，因此，可以把这个LoaderTask对象作为参数传给sWorker.post函数。在sWorker.post函数里面，会把这个LoaderTask对象封装成一个消息，并且放入这个子线程的消息队列中去。当这个子线程的消息循环处理这个消息的时候，就会调用这个LoaderTask对象的run函数，因此，我们就可以在LoaderTask对象的run函数中通过调用loadAndBindAllApps来执行加载系统中已经安装的应用程序的任务了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;了解了HanderThread类的使用方法之后，我们就可以重点地来分析它的实现了：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_12_3422643"  code_snippet_id="130106" snippet_file_name="blog_20131227_12_3422643" name="code" class="java">public class HandlerThread extends Thread {
	......
	private Looper mLooper;

	public HandlerThread(String name) {
		super(name);
		......
	}

	......

	public void run() {
		......
		Looper.prepare();
		synchronized (this) {
			mLooper = Looper.myLooper();
			......
		}
		......
		Looper.loop();
		......
	}

	public Looper getLooper() {
		......
		return mLooper;
	}

	......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;首先我们看到的是，HandlerThread类继承了Thread类，因此，通过它可以在应用程序中创建一个子线程，其次我们看到在它的run函数中，会进入一个消息循环中，因此，这个子线程可以常驻在应用程序中，直到它接收收到一个退出消息为止。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在run函数中，首先是调用Looper类的静态成员函数prepare来准备一个消息循环对象：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_13_4328497"  code_snippet_id="130106" snippet_file_name="blog_20131227_13_4328497" name="code" class="java">Looper.prepare();</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;然后通过Looper类的myLooper成员函数将这个子线程中的消息循环对象保存在HandlerThread类中的成员变量mLooper中：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_14_7850508"  code_snippet_id="130106" snippet_file_name="blog_20131227_14_7850508" name="code" class="java">mLooper = Looper.myLooper();</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这样，其它地方就可以方便地通过它的getLooper函数来获得这个消息循环对象了，有了这个消息循环对象后，就可以往这个子线程的消息队列中发送消息，通知这个子线程执行特定的任务了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;最在这个run函数通过Looper类的loop函数进入消息循环中：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_15_7468824"  code_snippet_id="130106" snippet_file_name="blog_20131227_15_7468824" name="code" class="java" style="margin-top: 4px; margin-right: 0px; margin-bottom: 4px; margin-left: 0px; background-color: rgb(240, 240, 240); ">Looper.loop();</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这样，一个具有消息循环的应用程序子线程就准备就绪了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;HandlerThread类的实现虽然非常简单，当然这得益于Java提供的Thread类和Android自己本身提供的Looper类，但是它的想法却非常周到，为应用程序开发人员提供了很大的方便。<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;3. 需要与UI交互的应用程序子线程消息模型<br /></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;前面说过，我们开发应用程序的时候，经常中需要创建一个子线程来在后台执行一个特定的计算任务，而在这个任务计算的过程中，需要不断地将计算进度或者计算结果展现在应用程序的界面中。典型的例子是从网上下载文件，为了不阻塞应用程序的主线程，我们开辟一个子线程来执行下载任务，子线程在下载的同时不断地将下载进度在应用程序界面上显示出来，这样做出来程序就非常友好。由于子线程不能直接操作应用程序的UI，因此，这时候，我们就可以通过往应用程序的主线程中发送消息来通知应用程序主线程更新界面上的下载进度。因为类似的这种情景在实际开发中经常碰到，Android系统为开发人员提供了一个异步任务类（AsyncTask）来实现上面所说的功能，即它会在一个子线程中执行计算任务，同时通过主线程的消息循环来获得更新应用程序界面的机会。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;为了更好地分析AsyncTask的实现，我们先举一个例子来说明它的用法。在前面一篇文章<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6730748">Android系统中的广播（Broadcast）机制简要介绍和学习计划</a>中，我们开发了一个应用程序Broadcast，其中使用了AsyncTask来在一个线程在后台在执行计数任务，计数过程通过广播（Broadcast）来将中间结果在应用程序界面上显示出来。在这个例子中，使用广播来在应用程序主线程和子线程中传递数据不是最优的方法，当时只是为了分析Android系统的广播机制而有意为之的。在本节内容中，我们稍微这个例子作一个简单的修改，就可以通过消息的方式来将计数过程的中间结果在应用程序界面上显示出来。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;为了区别<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6730748">Android系统中的广播（Broadcast）机制简要介绍和学习计划</a>一文中使用的应用程序Broadcast，我们将本节中使用的应用程序命名为Counter。首先在Android源代码工程中创建一个Android应用程序工程，名字就为Counter，放在packages/experimental目录下。关于如何获得Android源代码工程，请参考<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6559955">在Ubuntu上下载、编译和安装Android最新源代码</a>一文；关于如何在Android源代码工程中创建应用程序工程，请参考<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6580267">在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务</a>一文。这个应用程序工程定义了一个名为shy.luo.counter的package，这个例子的源代码主要就是实现在这个目录下的Counter.java文件中：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_16_2834781"  code_snippet_id="130106" snippet_file_name="blog_20131227_16_2834781" name="code" class="java">package shy.luo.counter;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class Counter extends Activity implements OnClickListener {
	private final static String LOG_TAG = &quot;shy.luo.counter.Counter&quot;;

	private Button startButton = null;
	private Button stopButton = null;
	private TextView counterText = null;

	private AsyncTask&lt;Integer, Integer, Integer&gt; task = null;
	private boolean stop = false;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		startButton = (Button)findViewById(R.id.button_start);
		stopButton = (Button)findViewById(R.id.button_stop);
		counterText = (TextView)findViewById(R.id.textview_counter);

		startButton.setOnClickListener(this);
		stopButton.setOnClickListener(this);

		startButton.setEnabled(true);
		stopButton.setEnabled(false);


		Log.i(LOG_TAG, &quot;Main Activity Created.&quot;);
	}


	@Override
	public void onClick(View v) {
		if(v.equals(startButton)) {
			if(task == null) {
				task = new CounterTask();
				task.execute(0);

				startButton.setEnabled(false);
				stopButton.setEnabled(true);
			}
		} else if(v.equals(stopButton)) {
			if(task != null) {
				stop = true;
				task = null;

				startButton.setEnabled(true);
				stopButton.setEnabled(false);
			}
		}
	}

	class CounterTask extends AsyncTask&lt;Integer, Integer, Integer&gt; {
		@Override
		protected Integer doInBackground(Integer... vals) {
			Integer initCounter = vals[0];

			stop = false;
			while(!stop) {
				publishProgress(initCounter);

				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				initCounter++;
			}

			return initCounter;
		}

		@Override
		protected void onProgressUpdate(Integer... values) {
			super.onProgressUpdate(values);

			String text = values[0].toString();
			counterText.setText(text);
		}

		@Override
		protected void onPostExecute(Integer val) {
			String text = val.toString();
			counterText.setText(text);
		}
	};
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个计数器程序很简单，它在界面上有两个按钮Start和Stop。点击Start按钮时，便会创建一个CounterTask实例task，然后调用它的execute函数就可以在应用程序中启动一个子线程，并且通过调用这个CounterTask类的doInBackground函数来执行计数任务。在计数的过程中，会通过调用publishProgress函数来将中间结果传递到onProgressUpdate函数中去，在onProgressUpdate函数中，就可以把中间结果显示在应用程序界面了。点击Stop按钮时，便会通过设置变量stop为true，这样，CounterTask类的doInBackground函数便会退出循环，然后将结果返回到onPostExecute函数中去，在onPostExecute函数，会把最终计数结果显示在用程序界面中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 在这个例子中，我们需要注意的是：</p><p>&nbsp;&nbsp; &nbsp; &nbsp; A. CounterTask类继承于AsyncTask类，因此它也是一个异步任务类；</p><p>&nbsp;&nbsp; &nbsp; &nbsp; B.&nbsp;CounterTask类的doInBackground函数是在后台的子线程中运行的，这时候它不可以操作应用程序的界面；</p><p>&nbsp;&nbsp; &nbsp; &nbsp; C.&nbsp;CounterTask类的onProgressUpdate和onPostExecute两个函数是应用程序的主线程中执行，它们可以操作应用程序的界面。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 关于C这一点的实现原理，我们在后面会分析到，这里我们先完整地介绍这个例子，以便读者可以参考做一下实验。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 接下来我们再看看应用程序的配置文件AndroidManifest.xml：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_17_2453097"  code_snippet_id="130106" snippet_file_name="blog_20131227_17_2453097" name="code" class="html">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
      package=&quot;shy.luo.counter&quot;
      android:versionCode=&quot;1&quot;
      android:versionName=&quot;1.0&quot;&gt;
    &lt;application android:icon=&quot;@drawable/icon&quot; android:label=&quot;@string/app_name&quot;&gt;
        &lt;activity android:name=&quot;.Counter&quot;
                  android:label=&quot;@string/app_name&quot;&gt;
            &lt;intent-filter&gt;
                &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
                &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
            &lt;/intent-filter&gt;
        &lt;/activity&gt;
    &lt;/application&gt;
&lt;/manifest&gt;</pre>&nbsp;&nbsp; &nbsp; &nbsp; 这个配置文件很简单，我们就不介绍了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;再来看应用程序的界面文件，它定义在res/layout/main.xml文件中：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_18_6182851"  code_snippet_id="130106" snippet_file_name="blog_20131227_18_6182851" name="code" class="html">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;  
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;  
    android:orientation=&quot;vertical&quot;  
    android:layout_width=&quot;fill_parent&quot;  
    android:layout_height=&quot;fill_parent&quot;   
    android:gravity=&quot;center&quot;&gt;  
    &lt;LinearLayout  
        android:layout_width=&quot;fill_parent&quot;  
        android:layout_height=&quot;wrap_content&quot;  
        android:layout_marginBottom=&quot;10px&quot;  
        android:orientation=&quot;horizontal&quot;   
        android:gravity=&quot;center&quot;&gt;  
        &lt;TextView    
        android:layout_width=&quot;wrap_content&quot;   
            android:layout_height=&quot;wrap_content&quot;   
            android:layout_marginRight=&quot;4px&quot;  
            android:gravity=&quot;center&quot;  
            android:text=&quot;@string/counter&quot;&gt;  
        &lt;/TextView&gt;  
        &lt;TextView    
            android:id=&quot;@+id/textview_counter&quot;  
        android:layout_width=&quot;wrap_content&quot;   
            android:layout_height=&quot;wrap_content&quot;   
            android:gravity=&quot;center&quot;  
            android:text=&quot;0&quot;&gt;  
        &lt;/TextView&gt;  
    &lt;/LinearLayout&gt;  
    &lt;LinearLayout  
        android:layout_width=&quot;fill_parent&quot;  
        android:layout_height=&quot;wrap_content&quot;  
        android:orientation=&quot;horizontal&quot;   
        android:gravity=&quot;center&quot;&gt;  
        &lt;Button   
            android:id=&quot;@+id/button_start&quot;  
            android:layout_width=&quot;wrap_content&quot;  
            android:layout_height=&quot;wrap_content&quot;  
            android:gravity=&quot;center&quot;  
            android:text=&quot;@string/start&quot;&gt;  
        &lt;/Button&gt;  
        &lt;Button   
            android:id=&quot;@+id/button_stop&quot;  
            android:layout_width=&quot;wrap_content&quot;  
            android:layout_height=&quot;wrap_content&quot;  
            android:gravity=&quot;center&quot;  
            android:text=&quot;@string/stop&quot; &gt;  
        &lt;/Button&gt;  
     &lt;/LinearLayout&gt;    
&lt;/LinearLayout&gt;  </pre>&nbsp;&nbsp; &nbsp; &nbsp; 这个界面配置文件也很简单，等一下我们在模拟器把这个应用程序启动起来后，就可以看到它的截图了。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;应用程序用到的字符串资源文件位于res/values/strings.xml文件中：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_19_8625066"  code_snippet_id="130106" snippet_file_name="blog_20131227_19_8625066" name="code" class="html">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;  
&lt;resources&gt;  
    &lt;string name=&quot;app_name&quot;&gt;Counter&lt;/string&gt;  
    &lt;string name=&quot;counter&quot;&gt;Counter: &lt;/string&gt;  
    &lt;string name=&quot;start&quot;&gt;Start Counter&lt;/string&gt;  
    &lt;string name=&quot;stop&quot;&gt;Stop Counter&lt;/string&gt;  
&lt;/resources&gt; </pre>&nbsp;&nbsp; &nbsp; &nbsp; 最后，我们还要在工程目录下放置一个编译脚本文件Android.mk：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_20_4514411"  code_snippet_id="130106" snippet_file_name="blog_20131227_20_4514411" name="code" class="html">LOCAL_PATH:= $(call my-dir)        
include $(CLEAR_VARS)        
        
LOCAL_MODULE_TAGS := optional        
        
LOCAL_SRC_FILES := $(call all-subdir-java-files)        
        
LOCAL_PACKAGE_NAME := Counter        
        
include $(BUILD_PACKAGE)  </pre>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;接下来就要编译了。有关如何单独编译Android源代码工程的模块，以及如何打包system.img，请参考<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6566662">如何单独编译Android源代码中的模块</a>一文。<br />&nbsp;&nbsp; &nbsp; &nbsp; 执行以下命令进行编译和打包：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_21_9672624"  code_snippet_id="130106" snippet_file_name="blog_20131227_21_9672624" name="code" class="html">USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Counter          
USER-NAME@MACHINE-NAME:~/Android$ make snod</pre>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;这样，打包好的Android系统镜像文件system.img就包含我们前面创建的Counter应用程序了。<br />&nbsp;&nbsp; &nbsp; &nbsp; 再接下来，就是运行模拟器来运行我们的例子了。关于如何在Android源代码工程中运行模拟器，请参考<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6559955">在Ubuntu上下载、编译和安装Android最新源代码</a>一文。<br />&nbsp;&nbsp; &nbsp; &nbsp; 执行以下命令启动模拟器：<br /><pre code_snippet_id="130106" snippet_file_name="blog_20131227_22_3402379"  code_snippet_id="130106" snippet_file_name="blog_20131227_22_3402379" name="code" class="html">USER-NAME@MACHINE-NAME:~/Android$ emulator </pre>&nbsp;&nbsp; &nbsp; &nbsp; 最后我们就可以在Launcher中找到Counter应用程序图标，把它启动起来，点击Start按钮，就会看到应用程序界面上的计数器跑起来了：<p></p><p><img width="710" height="505" src="http://hi.csdn.net/attachment/201110/27/0_1319731144Ll99.gif" alt="" /><br /></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这样，使用AsyncTask的例子就介绍完了，下面，我们就要根据上面对AsyncTask的使用情况来重点分析它的实现了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;AsyncTask类定义在frameworks/base/core/java/android/os/AsyncTask.java文件中：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_23_2115624"  code_snippet_id="130106" snippet_file_name="blog_20131227_23_2115624" name="code" class="java">public abstract class AsyncTask&lt;Params, Progress, Result&gt; {
	......

	private static final BlockingQueue&lt;Runnable&gt; sWorkQueue =
			new LinkedBlockingQueue&lt;Runnable&gt;(10);

	private static final ThreadFactory sThreadFactory = new ThreadFactory() {
		private final AtomicInteger mCount = new AtomicInteger(1);

		public Thread newThread(Runnable r) {
			return new Thread(r, &quot;AsyncTask #&quot; + mCount.getAndIncrement());
		}
	};

	......

	private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
		MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

	private static final int MESSAGE_POST_RESULT = 0x1;
	private static final int MESSAGE_POST_PROGRESS = 0x2;
	private static final int MESSAGE_POST_CANCEL = 0x3;

	private static final InternalHandler sHandler = new InternalHandler();

	private final WorkerRunnable&lt;Params, Result&gt; mWorker;
	private final FutureTask&lt;Result&gt; mFuture;

	......

	public AsyncTask() {
		mWorker = new WorkerRunnable&lt;Params, Result&gt;() {
			public Result call() throws Exception {
				......
				return doInBackground(mParams);
			}
		};

		mFuture = new FutureTask&lt;Result&gt;(mWorker) {
			@Override
			protected void done() {
				Message message;
				Result result = null;

				try {
					result = get();
				} catch (InterruptedException e) {
					android.util.Log.w(LOG_TAG, e);
				} catch (ExecutionException e) {
					throw new RuntimeException(&quot;An error occured while executing doInBackground()&quot;,
						e.getCause());
				} catch (CancellationException e) {
					message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
						new AsyncTaskResult&lt;Result&gt;(AsyncTask.this, (Result[]) null));
					message.sendToTarget();
					return;
				} catch (Throwable t) {
					throw new RuntimeException(&quot;An error occured while executing &quot;
						+ &quot;doInBackground()&quot;, t);
				}

				message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
					new AsyncTaskResult&lt;Result&gt;(AsyncTask.this, result));
				message.sendToTarget();
			}
		};
	}

	......

	public final Result get() throws InterruptedException, ExecutionException {
		return mFuture.get();
	}

	......

	public final AsyncTask&lt;Params, Progress, Result&gt; execute(Params... params) {
		......

		mWorker.mParams = params;
		sExecutor.execute(mFuture);

		return this;
	}

	......

	protected final void publishProgress(Progress... values) {
		sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
			new AsyncTaskResult&lt;Progress&gt;(this, values)).sendToTarget();
	}

        private void finish(Result result) {
                ......
                onPostExecute(result);
                ......
        }

	......

	private static class InternalHandler extends Handler {
		@SuppressWarnings({&quot;unchecked&quot;, &quot;RawUseOfParameterizedType&quot;})
		@Override
		public void handleMessage(Message msg) {
			AsyncTaskResult result = (AsyncTaskResult) msg.obj;
			switch (msg.what) {
		        case MESSAGE_POST_RESULT:
			     // There is only one result
			     result.mTask.finish(result.mData[0]);
			     break;
		        case MESSAGE_POST_PROGRESS:
			     result.mTask.onProgressUpdate(result.mData);
			     break;
		        case MESSAGE_POST_CANCEL:
			     result.mTask.onCancelled();
			     break;
			}
		}
	}

	private static abstract class WorkerRunnable&lt;Params, Result&gt; implements Callable&lt;Result&gt; {
		Params[] mParams;
	}

	private static class AsyncTaskResult&lt;Data&gt; {
		final AsyncTask mTask;
		final Data[] mData;

		AsyncTaskResult(AsyncTask task, Data... data) {
			mTask = task;
			mData = data;
		}
	}
}</pre><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;从AsyncTask的实现可以看出，当我们第一次创建一个AsyncTask对象时，首先会执行下面静态初始化代码创建一个线程池sExecutor：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_24_9749072"  code_snippet_id="130106" snippet_file_name="blog_20131227_24_9749072" name="code" class="java">private static final BlockingQueue&lt;Runnable&gt; sWorkQueue =
	new LinkedBlockingQueue&lt;Runnable&gt;(10);

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
	private final AtomicInteger mCount = new AtomicInteger(1);

	public Thread newThread(Runnable r) {
		return new Thread(r, &quot;AsyncTask #&quot; + mCount.getAndIncrement());
	}
};

......

private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
	MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里的ThreadPoolExecutor是Java提供的多线程机制之一，这里用的构造函数原型为：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_25_3478826"  code_snippet_id="130106" snippet_file_name="blog_20131227_25_3478826" name="code" class="java">ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, 
    BlockingQueue&lt;Runnable&gt; workQueue, ThreadFactory threadFactory)</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;各个参数的意义如下：<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;<strong><em>corePoolSize -- 线程池的核心线程数量</em></strong></p><p><strong><em>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;maximumPoolSize -- 线程池的最大线程数量</em></strong></p><p><strong><em>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;keepAliveTime -- 若线程池的线程数数量大于核心线程数量，那么空闲时间超过keepAliveTime的线程将被回收</em></strong></p><p><strong><em>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;unit -- 参数keepAliveTime使用的时间单位</em></strong></p><p><strong><em>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;workerQueue -- 工作任务队列</em></strong></p><p><strong><em>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;threadFactory -- 用来创建线程池中的线程</em></strong><br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;简单来说，ThreadPoolExecutor的运行机制是这样的：每一个工作任务用一个Runnable对象来表示，当我们要把一个工作任务交给这个线程池来执行的时候，就通过调用ThreadPoolExecutor的execute函数来把这个工作任务加入到线程池中去。此时，如果线程池中的线程数量小于corePoolSize，那么就会调用threadFactory接口来创建一个新的线程并且加入到线程池中去，再执行这个工作任务；如果线程池中的线程数量等于corePoolSize，但是工作任务队列workerQueue未满，则把这个工作任务加入到工作任务队列中去等待执行；如果线程池中的线程数量大于corePoolSize，但是小于maximumPoolSize，并且工作任务队列workerQueue已经满了，那么就会调用threadFactory接口来创建一个新的线程并且加入到线程池中去，再执行这个工作任务；如果线程池中的线程量已经等于maximumPoolSize了，并且工作任务队列workerQueue也已经满了，这个工作任务就被拒绝执行了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;创建好了线程池后，再创建一个消息处理器：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_26_1668683"  code_snippet_id="130106" snippet_file_name="blog_20131227_26_1668683" name="code" class="java">private static final InternalHandler sHandler = new InternalHandler();</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;注意，这行代码是在应用程序的主线程中执行的，因此，这个消息处理器sHandler内部引用的消息循环对象looper是应用程序主线程的消息循环对象，消息处理器的实现机制具体可以参考前面一篇文章<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/6817933">Android应用程序消息处理机制（Looper、Handler）分析</a>。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;AsyncTask类的静态初始化代码执行完成之后，才开始创建AsyncTask对象，即执行AsyncTask类的构造函数：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_27_4842029"  code_snippet_id="130106" snippet_file_name="blog_20131227_27_4842029" name="code" class="java">public AsyncTask() {
	mWorker = new WorkerRunnable&lt;Params, Result&gt;() {
		public Result call() throws Exception {
			......
			return doInBackground(mParams);
		}
	};

	mFuture = new FutureTask&lt;Result&gt;(mWorker) {
		@Override
		protected void done() {
			Message message;
			Result result = null;

			try {
				result = get();
			} catch (InterruptedException e) {
				android.util.Log.w(LOG_TAG, e);
			} catch (ExecutionException e) {
				throw new RuntimeException(&quot;An error occured while executing doInBackground()&quot;,
					e.getCause());
			} catch (CancellationException e) {
				message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
					new AsyncTaskResult&lt;Result&gt;(AsyncTask.this, (Result[]) null));
				message.sendToTarget();
				return;
			} catch (Throwable t) {
				throw new RuntimeException(&quot;An error occured while executing &quot;
					+ &quot;doInBackground()&quot;, t);
			}

			message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
				new AsyncTaskResult&lt;Result&gt;(AsyncTask.this, result));
			message.sendToTarget();
		}
	};
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在AsyncTask类的构造函数里面，主要是创建了两个对象，分别是一个WorkerRunnable对象mWorker和一个FutureTask对象mFuture。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;WorkerRunnable类实现了Callable接口，此外，它的内部成员变量mParams用于保存从AsyncTask对象的execute函数传进来的参数列表：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_28_4668088"  code_snippet_id="130106" snippet_file_name="blog_20131227_28_4668088" name="code" class="java">private static abstract class WorkerRunnable&lt;Params, Result&gt; implements Callable&lt;Result&gt; {
	Params[] mParams;
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;FutureTask类也实现了Runnable接口，所以它可以作为一个工作任务通过调用AsyncTask类的execute函数添加到sExecuto线程池中去：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_29_7318047"  code_snippet_id="130106" snippet_file_name="blog_20131227_29_7318047" name="code" class="java">public final AsyncTask&lt;Params, Progress, Result&gt; execute(Params... params) {
	......

	mWorker.mParams = params;
	sExecutor.execute(mFuture);

	return this;
}</pre><p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这里的FutureTask对象mFuture是用来封装前面的WorkerRunnable对象mWorker。当mFuture加入到线程池中执行时，它调用的是mWorker对象的call函数：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_30_2999649"  code_snippet_id="130106" snippet_file_name="blog_20131227_30_2999649" name="code" class="java">mWorker = new WorkerRunnable&lt;Params, Result&gt;() {
	public Result call() throws Exception {
	       ......
	       return doInBackground(mParams);
        }
};</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在call函数里面，会调用AsyncTask类的doInBackground函数来执行真正的任务，这个函数是要由AsyncTask的子类来实现的，注意，这个函数是在应用程序的子线程中执行的，它不可以操作应用程序的界面。<br /><p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;我们可以通过mFuture对象来操作当前执行的任务，例如查询当前任务的状态，它是正在执行中，还是完成了，还是被取消了，如果是完成了，还可以通过它获得任务的执行结果，如果还没有完成，可以取消任务的执行。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;当工作任务mWorker执行完成的时候，mFuture对象中的done函数就会被被调用，根据任务的完成状况，执行相应的操作，例如，如果是因为异常而完成时，就会抛异常，如果是正常完成，就会把任务执行结果封装成一个AsyncTaskResult对象：</p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_31_109710"  code_snippet_id="130106" snippet_file_name="blog_20131227_31_109710" name="code" class="java">private static class AsyncTaskResult&lt;Data&gt; {
	final AsyncTask mTask;
	final Data[] mData;

	AsyncTaskResult(AsyncTask task, Data... data) {
		mTask = task;
		mData = data;
	}
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;其中，成员变量mData保存的是任务执行结果，而成员变量mTask指向前面我们创建的AsyncTask对象。<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;最后把这个AsyncTaskResult对象封装成一个消息，并且通过消息处理器sHandler加入到应用程序主线程的消息队列中：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_32_6904126"  code_snippet_id="130106" snippet_file_name="blog_20131227_32_6904126" name="code" class="java">message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
	new AsyncTaskResult&lt;Result&gt;(AsyncTask.this, result));
message.sendToTarget();</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个消息最终就会在InternalHandler类的handleMessage函数中处理了：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_33_426138"  code_snippet_id="130106" snippet_file_name="blog_20131227_33_426138" name="code" class="java">private static class InternalHandler extends Handler {
	@SuppressWarnings({&quot;unchecked&quot;, &quot;RawUseOfParameterizedType&quot;})
	@Override
	public void handleMessage(Message msg) {
		AsyncTaskResult result = (AsyncTaskResult) msg.obj;
		switch (msg.what) {
		case MESSAGE_POST_RESULT:
			// There is only one result
			result.mTask.finish(result.mData[0]);
			break;
		......
		}
	}
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在这个函数里面，最终会调用前面创建的这个AsyncTask对象的finish函数来进一步处理：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_34_567841"  code_snippet_id="130106" snippet_file_name="blog_20131227_34_567841" name="code" class="java">private void finish(Result result) {
       ......
       onPostExecute(result);
       ......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数调用AsyncTask类的onPostExecute函数来进一步处理，AsyncTask类的onPostExecute函数一般是要由其子类来重载的，注意，这个函数是在应用程序的主线程中执行的，因此，它可以操作应用程序的界面。<br />&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在任务执行的过程当中，即执行doInBackground函数时候，可能通过调用publishProgress函数来将中间结果封装成一个消息发送到应用程序主线程中的消息队列中去：<p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_35_9837492"  code_snippet_id="130106" snippet_file_name="blog_20131227_35_9837492" name="code" class="java">protected final void publishProgress(Progress... values) {
	sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
		new AsyncTaskResult&lt;Progress&gt;(this, values)).sendToTarget();
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个消息最终也是由InternalHandler类的handleMessage函数来处理的：<br /><p></p><p></p><pre code_snippet_id="130106" snippet_file_name="blog_20131227_36_9663552"  code_snippet_id="130106" snippet_file_name="blog_20131227_36_9663552" name="code" class="java">private static class InternalHandler extends Handler {
	@SuppressWarnings({&quot;unchecked&quot;, &quot;RawUseOfParameterizedType&quot;})
	@Override
	public void handleMessage(Message msg) {
		AsyncTaskResult result = (AsyncTaskResult) msg.obj;
		switch (msg.what) {
		......
		case MESSAGE_POST_PROGRESS:
			     result.mTask.onProgressUpdate(result.mData);
			     break;
		......
		}
	}
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这里它调用前面创建的AsyncTask对象的onPorgressUpdate函数来进一步处理，这个函数一般是由AsyncTask的子类来实现的，注意，这个函数是在应用程序的主线程中执行的，因此，它和前面的onPostExecute函数一样，可以操作应用程序的界面。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这样，AsyncTask类的主要实现就介绍完了，结合前面开发的应用程序Counter来分析，会更好地理解它的实现原理。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 至此，Android应用程序线程消息循环模型就分析完成了，理解它有利于我们在开发Android应用程序时，能够充分利用多线程的并发性来提高应用程序的性能以及获得良好的用户体验。</p><p style="text-align: center;"><span style="font-size:14px;color:#cc0000;"><strong>老罗的新浪微博：<a target=_blank target="_blank" href="http://weibo.com/shengyangluo">http://weibo.com/shengyangluo</a>，欢迎关注！</strong></span><br /></p><p style="text-align: center;"></p>
</div>



<!-- Baidu Button BEGIN -->
<div id="bdshare" class="bdshare_t bds_tools get-codes-bdshare" style="float: right;">
<a class="bds_qzone"></a>
<a class="bds_tsina"></a>
<a class="bds_tqq"></a>
<a class="bds_renren"></a>
<a class="bds_t163"></a>
<span class="bds_more">更多</span>
<a class="shareCount"></a>
</div>
<!-- Baidu Button END -->


<!--192.168.100.33-->
<div class="article_next_prev">
            <li class="prev_article"><span>上一篇：</span><a href="/luoshengyang/article/details/6882903" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_shangyipian'])">Android应用程序键盘（Keyboard）消息处理机制分析</a></li>
            <li class="next_article"><span>下一篇：</span><a href="/luoshengyang/article/details/6939890" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_xiayipian'])">Android系统匿名共享内存（Anonymous Shared Memory）C++调用接口分析</a></li>
</div>

<!-- Baidu Button BEGIN -->
<script type="text/javascript" id="bdshare_js" data="type=tools&amp;uid=1536434" ></script>
<script type="text/javascript" id="bdshell_js"></script>
<script type="text/javascript">
    document.getElementById("bdshell_js").src = "http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion=" + Math.ceil(new Date()/3600000)
</script>
<!-- Baidu Button END -->

        <div id="digg" ArticleId="6905587">
            <dl id="btnDigg" class="digg digg_disable">
                <dt onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_ding'])">顶</dt>
                <dd>25</dd>
            </dl>
            <dl id="btnBury" class="digg digg_disable">
                <dt onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_cai'])">踩</dt>
                <dd>1</dd>
            </dl>
        </div>
</div>
      <dl class="blog-associat-tag">
        <dt>相关主题推荐</dt>
        <dd>
                <a href="http://www.csdn.net/tag/android应用" target="_blank">android应用</a> 
                <a href="http://www.csdn.net/tag/线程" target="_blank">线程</a> 
                <a href="http://www.csdn.net/tag/broadcastreceiver" target="_blank">broadcastreceiver</a> 
                <a href="http://www.csdn.net/tag/开发人员" target="_blank">开发人员</a> 
                <a href="http://www.csdn.net/tag/用户体验" target="_blank">用户体验</a> 
        </dd>
    </dl> 
    <dl class="blog-ass-articl">
        <dt>相关博文推荐</dt>
            <dd>
                <a href="http://blog.csdn.net/d1006799177/article/details/21113199" title="黑马程序员日记二线程  synchronized" target="_blank">黑马程序员日记二线程  synchron...</a></dd>
            <dd>
                <a href="http://blog.csdn.net/rwecho/article/details/21107199" title="Google Play注册开发者账号以及商业账号攻略" target="_blank">Google Play注册开发者账号以及...</a></dd>
            <dd>
                <a href="http://blog.csdn.net/dulei294948/article/details/21103023" title="正确的调用系统命令——为Process.waitFor设置超时以及其他" target="_blank">正确的调用系统命令——为Process....</a></dd>
            <dd>
                <a href="http://blog.csdn.net/ccssddnnbbookkee/article/details/21092595" title="第三章 使用 JDK 并发包构建程序" target="_blank">第三章 使用 JDK 并发包构建程序</a></dd>
            <dd>
                <a href="http://blog.csdn.net/dutianmin/article/details/21094381" title="python 线程中join方法的使用" target="_blank">python 线程中join方法的使用</a></dd>
            <dd>
                <a href="http://blog.csdn.net/ccy0815ccy/article/details/20737171" title="演绎多线程.第一往篇" target="_blank">演绎多线程.第一往篇</a></dd>
            <dd>
                <a href="http://blog.csdn.net/yangbingzhou/article/details/21089063" title="如何正确的关闭 MFC 线程" target="_blank">如何正确的关闭 MFC 线程</a></dd>
            <dd>
                <a href="http://blog.csdn.net/bluestar9/article/details/21087007" title="android 唤醒屏幕" target="_blank">android 唤醒屏幕</a></dd>
    </dl>

    <div id="ad_cen">
        <script type="text/javascript">            BAIDU_CLB_SLOT_ID = "117306";</script>
        <script type="text/javascript" src="http://cbjs.baidu.com/js/o.js"></script>
    </div>
    <script type="text/javascript">
        //new Ad(4, 'ad_cen');
    </script>
<div id="comment_title" class="panel_head">
    查看评论<a name="comments"></a></div>
<div id="comment_list">
</div>
<div id="comment_bar">
</div>
<div id="comment_form">
</div>
<div class="announce">
    * 以上用户言论只代表其个人观点，不代表CSDN网站的观点或立场<a name="reply"></a><a name="quote"></a></div>
<script type="text/javascript">
    var fileName = '6905587';
    var commentscount = 36;
    var islock = false
</script>
<script type="text/javascript" src="http://static.blog.csdn.net/scripts/comment.js"></script>
    <div id="ad_bot">
    </div>
    <script type="text/javascript">
    new Ad(5, 'ad_bot');
    </script>
<div id="report_dialog">
</div>
<div id="d-top" style="display: none;">
    <a id="d-top-a" href="#" title="回到顶部" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_huidaodingbu'])">
        <img src="http://static.blog.csdn.net/images/top.png" alt="TOP" /></a>
</div>
<script type="text/javascript">
    $(function ()
    {
        var d_top = $('#d-top');
        document.onscroll = function ()
        {
            var scrTop = (document.body.scrollTop || document.documentElement.scrollTop);
            if (scrTop > 500)
            {
                d_top.show();
            } else
            {
                d_top.hide();
            }
        }
        $('#d-top-a').click(function ()
        {
            scrollTo(0, 0);
            this.blur();
            return false;
        });
    });
  
</script>
<style type="text/css">
    .tag_list
    {
        background: none repeat scroll 0 0 #FFFFFF;
        border: 1px solid #D7CBC1;
        color: #000000;
        font-size: 12px;
        line-height: 20px;
        list-style: none outside none;
        margin: 10px 2% 0 1%;
        padding: 1px;
    }
    .tag_list h5
    {
        background: none repeat scroll 0 0 #E0DBD3;
        color: #47381C;
        font-size: 12px;
        height: 24px;
        line-height: 24px;
        padding: 0 5px;
        margin: 0;
    }
    .tag_list h5 a
    {
        color: #47381C;
    }
    .classify
    {
        margin: 10px 0;
        padding: 4px 12px 8px;
    }
    .classify a
    {
        margin-right: 20px;
        white-space: nowrap;
    }
</style>
<div class="tag_list">
    <h5>
        <a href="http://www.csdn.net/tag/" target="_blank">核心技术类目</a></h5>
    <div class="classify">
<a title="全部主题" href="http://www.csdn.net/tag" target="_blank" onclick="LogClickCount(this,336);">全部主题</a>
<a title="Java" href="http://www.csdn.net/tag/Java" target="_blank" onclick="LogClickCount(this,336);">Java</a>
<a title="VPN" href="http://www.csdn.net/tag/vpn" target="_blank" onclick="LogClickCount(this,336);">VPN</a>
<a title="Android" href="http://www.csdn.net/tag/android" target="_blank" onclick="LogClickCount(this,336);">Android</a>
<a title="iOS" href="http://www.csdn.net/tag/ios" target="_blank" onclick="LogClickCount(this,336);">iOS</a>
<a title="ERP" href="http://www.csdn.net/tag/erp" target="_blank" onclick="LogClickCount(this,336);">ERP</a>
<a title="IE10" href="http://www.csdn.net/tag/ie10" target="_blank" onclick="LogClickCount(this,336);">IE10</a>
<a title="Eclipse" href="http://www.csdn.net/tag/eclipse" target="_blank" onclick="LogClickCount(this,336);">Eclipse</a>
<a title="CRM" href="http://www.csdn.net/tag/crm" target="_blank" onclick="LogClickCount(this,336);">CRM</a>
<a title="JavaScript" href="http://www.csdn.net/tag/javascript" target="_blank" onclick="LogClickCount(this,336);">JavaScript</a>
<a title="Ubuntu" href="http://www.csdn.net/tag/ubuntu" target="_blank" onclick="LogClickCount(this,336);">Ubuntu</a>
<a title="NFC" href="http://www.csdn.net/tag/nfc" target="_blank" onclick="LogClickCount(this,336);">NFC</a>
<a title="WAP" href="http://www.csdn.net/tag/wap" target="_blank" onclick="LogClickCount(this,336);">WAP</a>
<a title="jQuery" href="http://www.csdn.net/tag/jquery" target="_blank" onclick="LogClickCount(this,336);">jQuery</a>
<a title="数据库" href="http://www.csdn.net/tag/数据库" target="_blank" onclick="LogClickCount(this,336);">数据库</a>
<a title="BI" href="http://www.csdn.net/tag/bi" target="_blank" onclick="LogClickCount(this,336);">BI</a>
<a title="HTML5" href="http://www.csdn.net/tag/html5" target="_blank" onclick="LogClickCount(this,336);">HTML5</a>
<a title="Spring" href="http://www.csdn.net/tag/spring" target="_blank" onclick="LogClickCount(this,336);">Spring</a>
<a title="Apache" href="http://www.csdn.net/tag/apache" target="_blank" onclick="LogClickCount(this,336);">Apache</a>
<a title="Hadoop" href="http://www.csdn.net/tag/hadoop" target="_blank" onclick="LogClickCount(this,336);">Hadoop</a>
<a title=".NET" href="http://www.csdn.net/tag/.net" target="_blank" onclick="LogClickCount(this,336);">.NET</a>
<a title="API" href="http://www.csdn.net/tag/api" target="_blank" onclick="LogClickCount(this,336);">API</a>
<a title="HTML" href="http://www.csdn.net/tag/html" target="_blank" onclick="LogClickCount(this,336);">HTML</a>
<a title="SDK" href="http://www.csdn.net/tag/sdk" target="_blank" onclick="LogClickCount(this,336);">SDK</a>
<a title="IIS" href="http://www.csdn.net/tag/iis" target="_blank" onclick="LogClickCount(this,336);">IIS</a>
<a title="Fedora" href="http://www.csdn.net/tag/fedora" target="_blank" onclick="LogClickCount(this,336);">Fedora</a>
<a title="XML" href="http://www.csdn.net/tag/xml" target="_blank" onclick="LogClickCount(this,336);">XML</a>
<a title="LBS" href="http://www.csdn.net/tag/lbs" target="_blank" onclick="LogClickCount(this,336);">LBS</a>
<a title="Unity" href="http://www.csdn.net/tag/unity" target="_blank" onclick="LogClickCount(this,336);">Unity</a>
<a title="Splashtop" href="http://www.csdn.net/tag/splashtop" target="_blank" onclick="LogClickCount(this,336);">Splashtop</a>
<a title="UML" href="http://www.csdn.net/tag/uml" target="_blank" onclick="LogClickCount(this,336);">UML</a>
<a title="components" href="http://www.csdn.net/tag/components" target="_blank" onclick="LogClickCount(this,336);">components</a>
<a title="Windows Mobile" href="http://www.csdn.net/tag/windowsmobile" target="_blank" onclick="LogClickCount(this,336);">Windows Mobile</a>
<a title="Rails" href="http://www.csdn.net/tag/rails" target="_blank" onclick="LogClickCount(this,336);">Rails</a>
<a title="QEMU" href="http://www.csdn.net/tag/qemu" target="_blank" onclick="LogClickCount(this,336);">QEMU</a>
<a title="KDE" href="http://www.csdn.net/tag/kde" target="_blank" onclick="LogClickCount(this,336);">KDE</a>
<a title="Cassandra" href="http://www.csdn.net/tag/cassandra" target="_blank" onclick="LogClickCount(this,336);">Cassandra</a>
<a title="CloudStack" href="http://www.csdn.net/tag/cloudstack" target="_blank" onclick="LogClickCount(this,336);">CloudStack</a>
<a title="FTC" href="http://www.csdn.net/tag/ftc" target="_blank" onclick="LogClickCount(this,336);">FTC</a>
<a title="coremail" href="http://www.csdn.net/tag/coremail" target="_blank" onclick="LogClickCount(this,336);">coremail</a>
<a title="OPhone " href="http://www.csdn.net/tag/ophone " target="_blank" onclick="LogClickCount(this,336);">OPhone </a>
<a title="CouchBase" href="http://www.csdn.net/tag/couchbase" target="_blank" onclick="LogClickCount(this,336);">CouchBase</a>
<a title="云计算" href="http://www.csdn.net/tag/云计算" target="_blank" onclick="LogClickCount(this,336);">云计算</a>
<a title="iOS6" href="http://www.csdn.net/tag/iOS6" target="_blank" onclick="LogClickCount(this,336);">iOS6</a>
<a title="Rackspace " href="http://www.csdn.net/tag/rackspace " target="_blank" onclick="LogClickCount(this,336);">Rackspace </a>
<a title="Web App" href="http://www.csdn.net/tag/webapp" target="_blank" onclick="LogClickCount(this,336);">Web App</a>
<a title="SpringSide" href="http://www.csdn.net/tag/springside" target="_blank" onclick="LogClickCount(this,336);">SpringSide</a>
<a title="Maemo" href="http://www.csdn.net/tag/maemo" target="_blank" onclick="LogClickCount(this,336);">Maemo</a>
<a title="Compuware" href="http://www.csdn.net/tag/compuware" target="_blank" onclick="LogClickCount(this,336);">Compuware</a>
<a title="大数据" href="http://www.csdn.net/tag/大数据" target="_blank" onclick="LogClickCount(this,336);">大数据</a>
<a title="aptech" href="http://www.csdn.net/tag/aptech" target="_blank" onclick="LogClickCount(this,336);">aptech</a>
<a title="Perl" href="http://www.csdn.net/tag/perl" target="_blank" onclick="LogClickCount(this,336);">Perl</a>
<a title="Tornado" href="http://www.csdn.net/tag/tornado" target="_blank" onclick="LogClickCount(this,336);">Tornado</a>
<a title="Ruby" href="http://www.csdn.net/tag/ruby" target="_blank" onclick="LogClickCount(this,336);">Ruby</a>
<a title="Hibernate" href="http://www.csdn.net/hibernate" target="_blank" onclick="LogClickCount(this,336);">Hibernate</a>
<a title="ThinkPHP" href="http://www.csdn.net/tag/thinkphp" target="_blank" onclick="LogClickCount(this,336);">ThinkPHP</a>
<a title="Spark" href="http://www.csdn.net/tag/spark" target="_blank" onclick="LogClickCount(this,336);">Spark</a>
<a title="HBase" href="http://www.csdn.net/tag/hbase" target="_blank" onclick="LogClickCount(this,336);">HBase</a>
<a title="Pure" href="http://www.csdn.net/tag/pure" target="_blank" onclick="LogClickCount(this,336);">Pure</a>
<a title="Solr" href="http://www.csdn.net/tag/solr" target="_blank" onclick="LogClickCount(this,336);">Solr</a>
<a title="Angular" href="http://www.csdn.net/tag/angular" target="_blank" onclick="LogClickCount(this,336);">Angular</a>
<a title="Cloud Foundry" href="http://www.csdn.net/tag/cloudfoundry" target="_blank" onclick="LogClickCount(this,336);">Cloud Foundry</a>
<a title="Redis" href="http://www.csdn.net/tag/redis" target="_blank" onclick="LogClickCount(this,336);">Redis</a>
<a title="Scala" href="http://www.csdn.net/tag/scala" target="_blank" onclick="LogClickCount(this,336);">Scala</a>
<a title="Django" href="http://www.csdn.net/tag/django" target="_blank" onclick="LogClickCount(this,336);">Django</a>
<a title="Bootstrap" href="http://www.csdn.net/tag/bootstrap" target="_blank" onclick="LogClickCount(this,336);">Bootstrap</a>
    </div>
</div>

                    <div class="clear">
                    </div>
                </div>
            </div>
            <div id="side">
    <div class="side">
<div id="panel_Profile" class="panel">
<ul class="panel_head"><span>个人资料</span></ul>
<ul class="panel_body profile">
<div id="blog_userface">
    <a href="http://my.csdn.net/Luoshengyang" target="_blank">
    <img src="http://avatar.csdn.net/5/6/E/1_luoshengyang.jpg" title="访问我的空间" style="max-width:90%"/>
    </a>
    <br />
    <span><a href="http://my.csdn.net/Luoshengyang" class="user_name" target="_blank">Luoshengyang</a></span>
</div>
<div class="interact">
<a href="javascript:void(0);" class="attent" id="span_add_follow" title="[加关注]" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_guanzhu'])"></a>
<a href="javascript:void(0);" class="letter" onclick="loginto(1)" title="[发私信]" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_sixin'])"></a>
</div>
<div id="blog_medal">
        <div class="ico_expert" title="CSDN认证专家"></div>
</div>
<ul id="blog_rank">
    <li>访问：<span>3159716次</span></li>
    <li>积分：<span>16447分</span></li>
    <li>排名：<span>第129名</span></li>
</ul>
<ul id="blog_statistics">
    <li>原创：<span>100篇</span></li>
    <li>转载：<span>0篇</span></li>
    <li>译文：<span>0篇</span></li>
    <li>评论：<span>5212条</span></li>
</ul>
</ul>
</div>


<div id="custom_column_6567056" class="panel">
<ul class="panel_head"><span>博客公告</span></ul>
<ul class="panel_body">
<b><font color="red" size="2">本博客所有文章均为原创，欢迎交流，欢迎转载；转载请勿篡改内容，并且注明出处，禁止用于商业目的，谢谢！</font></b>
</ul>
</div><div id="custom_column_21664874" class="panel">
<ul class="panel_head"><span>图书信息</span></ul>
<ul class="panel_body">
<font color="red"><b>书名：</b></font>
<p>《Android系统源代码情景分析》</p>

<p><a href="http://wenku.it168.com/d_000574719.shtml" target="_blank">试读请点击</a></p>

<p><font color="red"><b>出版社：</b></font></p>
<p>电子工业出版社</p>

<p><font color="red"><b>网店：</b></font></p>

<p>1. <a href="http://product.dangdang.com/product.aspx?product_id=22884086" target="_blank">当当网（满100减20，满200减50）</a></p><p></p>

<p>2. <a href="http://book.360buy.com/11104739.html" target="_blank">京东网（满99减20）</a></p><p></p>

<p>3. <a href="http://www.amazon.cn/Android%E7%B3%BB%E7%BB%9F%E6%BA%90%E4%BB%A3%E7%A0%81%E6%83%85%E6%99%AF%E5%88%86%E6%9E%90-%E7%BD%97%E5%8D%87%E9%98%B3/dp/B009OLU8EE" target="_blank">亚马逊网（满100减20，满200减50）</a></p><p></p>

<p>4. <a href="http://product.china-pub.com/3684413" target="_blank">互动出版网</a></p><p></p>

<p>5. <a href="http://www.suning.com/emall/prd_10052_22001_-7_3435777_.html" target="_blank">苏宁易购网</a></p><p></p>

<p><a href="http://s.etao.com/search?spm=1002.8.1.1386.g76Rk3&amp;q=Android%CF%B5%CD%B3%D4%B4%B4%FA%C2%EB%C7%E9%BE%B0%B7%D6%CE%F6&amp;style=list&amp;initiative_id=etao_20121011&amp;pn=1&amp;sort=sale-desc" target="_blank">比一比谁更实惠</a></p>

<p><font color="red"><b>号外：</b></font></p>

<p>本书繁体版已经成功输出到台湾</p>

<p></p>
</ul>
</div><div id="custom_column_21603386" class="panel">
<ul class="panel_head"><span>联系方式</span></ul>
<ul class="panel_body">
<font color="red"><b>新浪微博：</b></font>
<p><a href="http://weibo.com/shengyangluo" target="_blank"><font color="red"><b>http://weibo.com/shengyangluo</b></font></a></p><p></p>

<font color="red"><b>QQ交流群：</b></font>
<p><b><font color="red">130112760</font></b>（1000人群，已满）</p>
<p><b><font color="red">248749286</font></b>（1000人群，已满）</p>
<p><b><font color="red">204155322</font></b>（500人群，未满）</p>
<p><b><font color="red">278558417</font></b>（500人群，已满）</p>
<p><b><font color="red">303165655</font></b>（500人群，已满）</p>
<p>PS：请勿同时加入多个群，一经发现，永久封号，谢谢！</p>
</ul>
</div><div id="panel_Category" class="panel">
    <ul class="panel_head"><span>博客专栏</span></ul>
    <ul class="panel_body" id="sp_column">
    <table cellpadding="0" cellspacing="0"><tr>
    <td style="padding:10px 10px 0 0;">
    <a href="http://blog.csdn.net/column/details/androidluo.html" target="_blank"><img src="http://avatar.csdn.net/blogpic/20111107095438958.jpg" style="width:75px;height:75px;" /></a>
    </td>
    <td style="padding:10px 0; vertical-align:top;">
    <a href="http://blog.csdn.net/column/details/androidluo.html" target="_blank">老罗的Android之旅</a>
    <p>文章：100篇</p>
    <span>阅读：3163514</span>
    </td>
    </tr></table>
    </ul>
</div><div id="hotarticls" class="panel">
<ul class="panel_head"><span>阅读排行</span></ul>
<ul class="panel_body itemlist">
<li>
<a href="/luoshengyang/article/details/6559955" title="在Ubuntu上下载、编译和安装Android最新源代码">在Ubuntu上下载、编译和安装Android最新源代码</a><span>(109328)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6618363" title="Android进程间通信（IPC）机制Binder简要介绍和学习计划">Android进程间通信（IPC）机制Binder简要介绍和学习计划</a><span>(95202)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6689748" title="Android应用程序启动过程源代码分析">Android应用程序启动过程源代码分析</a><span>(80758)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6568411" title="在Ubuntu上为Android系统编写Linux内核驱动程序">在Ubuntu上为Android系统编写Linux内核驱动程序</a><span>(78562)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8923485" title="那两年炼就的Android内功修养">那两年炼就的Android内功修养</a><span>(76472)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6567257" title="Android硬件抽象层（HAL）概要介绍和学习计划">Android硬件抽象层（HAL）概要介绍和学习计划</a><span>(72470)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6564592" title="在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）">在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）</a><span>(66669)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6557518" title="Android学习启动篇">Android学习启动篇</a><span>(58983)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6566662" title="如何单独编译Android源代码中的模块">如何单独编译Android源代码中的模块</a><span>(57962)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6575988" title="在Ubuntu为Android硬件抽象层（HAL）模块编写JNI方法提供Java访问硬件服务接口">在Ubuntu为Android硬件抽象层（HAL）模块编写JNI方法提供Java访问硬件服务接口</a><span>(56695)</span>
</li>
</ul>
</div>
<div id="hotarticls2" class="panel">
<ul class="panel_head"><span>评论排行</span></ul>
<ul class="panel_body itemlist">
<li>
<a href="/luoshengyang/article/details/6689748" title="Android应用程序启动过程源代码分析">Android应用程序启动过程源代码分析</a><span>(322)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6559955" title="在Ubuntu上下载、编译和安装Android最新源代码">在Ubuntu上下载、编译和安装Android最新源代码</a><span>(238)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8116866" title="《Android系统源代码情景分析》一书勘误">《Android系统源代码情景分析》一书勘误</a><span>(208)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6882903" title="Android应用程序键盘（Keyboard）消息处理机制分析">Android应用程序键盘（Keyboard）消息处理机制分析</a><span>(182)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8923485" title="那两年炼就的Android内功修养">那两年炼就的Android内功修养</a><span>(181)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6580267" title="在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务">在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务</a><span>(170)</span>
</li>
<li>
<a href="/luoshengyang/article/details/7409491" title="Android博客文章整理">Android博客文章整理</a><span>(167)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6568411" title="在Ubuntu上为Android系统编写Linux内核驱动程序">在Ubuntu上为Android系统编写Linux内核驱动程序</a><span>(151)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8452527" title="2012年的Android之旅：梦想、学习、坚持、自信、淡定">2012年的Android之旅：梦想、学习、坚持、自信、淡定</a><span>(139)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6564592" title="在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）">在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）</a><span>(136)</span>
</li>
</ul>
</div>
<div id="newcomments" class="panel">
<ul class="panel_head"><span>最新评论</span></ul>
<ul class="panel_body itemlist">
    <li>
    <a href="/Luoshengyang/article/details/6568411#comments">在Ubuntu上为Android系统编写Linux内核驱动程序</a>
    <p style="margin:0px;"><a href="/HuiyuYang_fish" class="user_name">HuiyuYang_fish</a>:
期待实验~
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6557518#comments">Android学习启动篇</a>
    <p style="margin:0px;"><a href="/HuiyuYang_fish" class="user_name">HuiyuYang_fish</a>:
貌似我也要开始android的初步涉猎了~
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/12957169#comments">《老罗的Android之旅》导读PPT</a>
    <p style="margin:0px;"><a href="/mianju23" class="user_name">mianju23</a>:
牛人
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/8452527#comments">2012年的Android之旅：梦想、学习、坚持、自信、淡定</a>
    <p style="margin:0px;"><a href="/wuxiaoming1733" class="user_name">wuxiaoming1733</a>:
顶楼主
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6737352#comments">Android应用程序注册广播接收器（registerReceiver）的过程分析</a>
    <p style="margin:0px;"><a href="/wuxiaoming1733" class="user_name">wuxiaoming1733</a>:
罗大神你好，我这边还没明白，怎么自己用代码，实现androidmanifest.xml里静态注册系统...
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6642463#comments">Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析</a>
    <p style="margin:0px;"><a href="/zqz19891031" class="user_name">zqz19891031</a>:
博主您好，看了你的博客，收获颇多！有一个问题想请教：最近在看Android的Binder实现，看到B...
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/8923485#comments">那两年炼就的Android内功修养</a>
    <p style="margin:0px;"><a href="/xin128888" class="user_name">xin128888</a>:
老罗，有问题能否指导下。目前我想学习驱动相关知识，但是不知道要看哪本入门书籍合适，目标主要是快速入门...
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/19023609#comments">Android源代码编译命令m/mm/mmm/make分析</a>
    <p style="margin:0px;"><a href="/soledadzz" class="user_name">soledadzz</a>:
您的文章已被推荐到CSDN首页，感谢您的分享。
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/19023609#comments">Android源代码编译命令m/mm/mmm/make分析</a>
    <p style="margin:0px;"><a href="/u013725065" class="user_name">u013725065</a>:

    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/19023609#comments">Android源代码编译命令m/mm/mmm/make分析</a>
    <p style="margin:0px;"><a href="/u013725065" class="user_name">u013725065</a>:

    </p>
    </li>
</ul>
</div>
    </div>
    <div class="clear">
    </div>
</div>

            <div class="clear">
            </div>
        </div>
        

<script type="text/javascript" src="http://static.blog.csdn.net/scripts/newblog.min.js"></script>
<script type="text/javascript" src="http://medal.blog.csdn.net/showblogmedal.ashx?blogid=1134701"></script>
<script type="text/javascript">
    document.write('<script type="text/javascript" src="http://csdnimg.cn/pubfooter/js/publib_footer.js?' + Math.floor(new Date() / 120000).toString(36) + '="></' + 'script>');
</script>
    <script type="text/javascript" src="http://passport.csdn.net/content/loginbox/login.js"></script>
<script type="text/javascript">document.write("<img src=http://counter.csdn.net/pv.aspx?id=24 border=0 width=0 height=0>");</script>
<script type="text/javascript" src="http://www.csdn.net/ui/scripts/Csdn/counter.js"></script>
<script type="text/javascript" src="http://ad.csdn.net/scripts/ad-blog.js"></script>
<script type="text/javascript" src="http://zz.csdn.net/js/count.js"></script>
<script type="text/javascript">
    $(function () {
        function __get_code_toolbar(snippet_id) {
            return $("<a href='https://code.csdn.net/snippets/"
                    + snippet_id
                    + "' target='_blank' title='在CODE上查看代码片' style='text-indent:0;'><img src='https://code.csdn.net/assets/CODE_ico.png' width=12 height=12 alt='在CODE上查看代码片' style='position:relative;top:1px;left:2px;'/></a>"
                    + "<a href='https://code.csdn.net/snippets/"
                    + snippet_id
                    + "/fork' target='_blank' title='派生到我的代码片'  style='text-indent:0;'><img src='https://code.csdn.net/assets/ico_fork.svg' width=12 height=12 alt='派生到我的代码片' style='position:relative;top:2px;left:2px;'/></a>");
        }
        
        $("[code_snippet_id]").each(function () {
            __s_id = $(this).attr("code_snippet_id");
            if (__s_id != null && __s_id != "" && __s_id != 0 && parseInt(__s_id) > 70020) {
                __code_tool = __get_code_toolbar(__s_id);
                $(this).prev().find(".tools").append(__code_tool);
            }
        });
    });
</script>

    </div>
      <!--new top-->
    
    <script id="csdn-toolbar-id" btnId="header_notice_num" wrapId="note1" count="5" subCount="5" type="text/javascript" src="http://static.csdn.net/public/common/toolbar/js/toolbar.js"></script>     <!--new top-->
</body>
</html>
